// 是实现一个简单的 HTTP 请求发送函数
import { AxiosPromise, AxiosRequestConfig, AxiosResponse } from "../types";
import { parseHeaders } from "../helpers/headers";
import { createError } from "../helpers/error";
import { isURLSameOrigin } from "../helpers/url";
import cookie from "../helpers/cookie";
import { isFormData } from "../helpers/util";

export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject) => {

    const {
      data = null,
      url,
      method,
      headers = {},
      responseType,
      timeout,
      cancelToken,
      withCredentials,
      xsrfCookieName, // Token 存放在哪个 Cookie 里（名称）
      xsrfHeaderName,  //Token 要写进哪个请求头里（名称）
      onDownloadProgress,
      onUploadProgress,
      auth,
      validateStatus
    } = config   // 从 config 对象中解构出 data、url 和 method 属性

    const request = new XMLHttpRequest()   // 创建 XMLHttpRequest 实例

    // 打开请求 toUpperCase() 把请求方法转换为大写，true 表示请求是异步的。
    request.open(method!.toUpperCase(), url!, true)

    // 配置请求基本信息
    configureRequest()

    // 绑定事件监听（状态变化、错误、超时、进度事件
    addEvents()

    // 处理请求头（注入 XSRF-Token、删除无效头）
    processHeaders()

    // 处理请求取消
    processCancel()

    // 发送请求  携带请求体（body）
    request.send(data)

    // 配置请求基本信息
    function configureRequest(): void {
      if (responseType) { // 设置响应类型
        request.responseType = responseType
      }

      if (timeout) {
        request.timeout = timeout
      }

      if (withCredentials) {// 允许跨域携带 Cookie
        request.withCredentials = withCredentials
      }
    }

    // 绑定事件监听
    function addEvents(): void {

      // 监听请求状态变化（readyState 从 0 到 4 变化）
      request.onreadystatechange = function handleLoad() {
        if (request.readyState !== 4) {// XHR 请求的生命周期状态（0-4），4 表示请求已完成且响应已就绪。
          return   // 只处理请求完成状态（4 表示已完成）
        }
        if (request.status === 0) {  // 请求未初始化（未调用 open）
          return  // 忽略未初始化的请求（如本地文件访问时可能出现）
        }

        // 解析响应头（将原生格式转换为对象，如 'Content-Type: application/json' 转为 { Content-Type: 'application/json' }）
        const responseHeaders = parseHeaders(request.getAllResponseHeaders())
        // 根据响应类型获取数据
        const responseData = responseType !== 'text' ? request.response : request.responseText
        // 构造符合 Axios 格式的响应对象
        const response: AxiosResponse = {
          data: responseData,
          status: request.status,
          statusText: request.statusText,
          headers: responseHeaders,
          config,
          request
        }
        handleResponse(response)
      }

      // 监听网络错误（如 DNS 失败、连接中断）
      request.onerror = function handleError() {
        reject(createError('Network Error', config, null, request))
      }

      request.ontimeout = function handleTimeout() {
        reject(createError(`Timeout of ${timeout} ms exceeded`, config, 'ECONNABORTED', request))
      }

      if (onDownloadProgress) {
        request.onprogress = onDownloadProgress
      }

      if (onUploadProgress) {
        request.upload.onprogress = onUploadProgress
      }

    }

    // 处理请求头
    function processHeaders(): void {
      if (isFormData(data)) {
        delete headers['Content-type']
      }

      // 自动注入 XSRF-Token 到请求头
      if ((withCredentials || isURLSameOrigin(url!)) && xsrfCookieName) {
        const xsrfValue = cookie.read(xsrfCookieName) // 从 cookie 中读取 XSRF-Token
        if (xsrfValue && xsrfHeaderName) {
          headers[xsrfHeaderName] = xsrfValue // 将 Token 写入请求头
        }
      }

      if (auth) { //  处理认证头
        headers['Authorization'] = 'Basic' + btoa(auth.username + ':' + auth.password)
      }

      //用于处理 HTTP 请求的 请求头（Headers），核心逻辑是：在没有请求体（data 为 null）时，删除 Content-Type 头，避免浏览器自动添加无效的默认值。
      Object.keys(headers).forEach((name) => {
        if (data === null && name.toLowerCase() === 'content-type') {
          delete headers[name]
        } else {
          request.setRequestHeader(name, headers[name])
        }
      })
    }

    // 处理请求取消
    function processCancel(): void {
      if (cancelToken) {
        // 当 cancelToken 的 promise 解析（即调用 cancel() 时）
        cancelToken.promise
          .then(reason => {
            request.abort()// 取消请求
            reject(reason) // 抛出取消错误
          }).catch(() => {
            /* istanbul ignore next */
            // do nothing
          })
      }
    }

    // 状态码验证
    function handleResponse(response: AxiosResponse): void {
      if (!validateStatus || validateStatus(response.status)) {
        resolve(response)
      } else {
        reject(createError(`Request failed with status code ${response.status}`, config, null, request, response))
      }
    }
  })
}